package com.me.mysqladmin.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;
import javax.sql.RowSet;
import javax.sql.rowset.CachedRowSet;

import com.me.mysqladmin.util.JDBCUtil;
import com.me.mysqladmin.util.NullUtil;
import com.me.mysqladmin.util.RowSetUtil;
import com.sun.rowset.CachedRowSetImpl;

/**
 * 数据库访问层的通用CRUD，所有DAO的基类
 * @author 周东
 * <p>创建日期：2015 2015年2月26日 下午11:33:22</p>
 */
@SuppressWarnings("restriction")
public abstract class BaseDAOImpl<T> {
	// 注入数据源
	@Resource
	protected DataSource dataSource;
	
	/*--------------------------通用CURD方法--------------------------*/
	
	/**
	 * 通用查询（可指定列，也可查询全部列）
	 * 注意：该方法不适合查询海量数据，因为每一条记录都会生成一个JavaBean对象存于内存中，消耗大量资源
	 * @return 实体集
	 * @throws SQLException
	 */
	protected List<T> query(String sql, Object... params) throws SQLException {
		// 方法入参判断
		if (NullUtil.existNull(params)) {
			return Collections.emptyList();
		}
		// 获取离线结果集
		RowSet rs = baseQuery(sql, params);
		// 返回结果
		return toEntities(rs);
	}

	/**
	 * 查询第1列至第n列的记录，只用于查询指定列
	 * @param maxCol 最大列数（从1开始）
	 * @return 返回所有记录，其中每条记录的列名用1,2,3,4……来标识
	 * @throws SQLException 
	 */
	protected List<Map<Integer,Object>> querySpecificCols(String sql, int maxCol, Object... params) throws SQLException{
		// 方法入参判断
		if (NullUtil.existNull(params)) {
			return Collections.emptyList();
		}
		// 获取离线结果集
		RowSet rs = baseQuery(sql, params);
		// 返回结果
		return RowSetUtil.toSimpleMapList(rs, maxCol);
	}
	
	/**
	 * 通用查询（可指定列，也可查询全部列）
	 * @return Map集合
	 * @throws SQLException 
	 */
	protected List<Map<String,Object>> queryMap(String sql, Object... params) throws SQLException{
		// 方法入参判断
		if (NullUtil.existNull(params)) {
			return Collections.emptyList();
		}
		// 获取离线结果集
		RowSet rs = baseQuery(sql, params);
		// 返回结果
		return RowSetUtil.toMapList(rs);
	}
	
	/**
	 * 最基本的查询操作
	 * @param sql
	 * @param params 问号参数
	 * @return 离线结果集对象
	 * @throws SQLException 
	 */
	private RowSet baseQuery(String sql, Object... params) throws SQLException{
		// 方法入参判断
		if (NullUtil.existNull(params)) {
			return null;
		}
		// 定义数据库操作相关对象
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		CachedRowSet rowset = new CachedRowSetImpl();
		try {
			conn = dataSource.getConnection();
			pstmt = conn.prepareStatement(sql);
			for (int i = 0; i < params.length; i++) {
				pstmt.setObject(i + 1, params[i]);
			}
			// 获得在线结果集
			rs = pstmt.executeQuery();
			// 将在线结果集填充到离线结果集
			rowset.populate(rs);
		} finally {
			JDBCUtil.free(rs, pstmt, conn);
		}
		return rowset;
	}
	
	/**
	 * DML操作
	 * @param sql
	 * @param params 问号参数
	 * @return 返回受影响的行数
	 */
	protected Integer baseDML(String sql, Object... params) throws SQLException{
		Integer line = 0;
		// 方法入参判断
		if (NullUtil.existNull(params)) {
			return line;
		}
		Connection conn = null;
		PreparedStatement pstmt = null;
		try {
			conn = dataSource.getConnection();
			pstmt = conn.prepareStatement(sql);
			for (int i = 0; i < params.length; i++) {
				pstmt.setObject(i + 1, params[i]);
			}
			line = pstmt.executeUpdate();
		} finally{
			// 释放资源
			JDBCUtil.free(pstmt, conn);
		}
		return line;
	}
	
	/**
	 * 直接执行SQL语句
	 * @param sql
	 * @throws Exception
	 */
	public Map<String, Object> executeAll(String sql) throws Exception{
		// 方法入参判断
		if (NullUtil.existNull(sql)) {
			return Collections.emptyMap();
		}
		// 封装返回值
		Map<String, Object> map = new LinkedHashMap<>();
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
        try {
        	conn = dataSource.getConnection();
        	stmt = conn.createStatement();
        	boolean hasResultSet = stmt.execute(sql);
        	if (hasResultSet) {
        		CachedRowSet rowset = new CachedRowSetImpl();
    			// 获得在线结果集
    			rs = stmt.getResultSet();
    			// 将在线结果集填充到离线结果集
    			rowset.populate(rs);
        		// 将离线结果集转换成ListMap
    			List<Map<String, Object>> listMap = RowSetUtil.toMapList(rowset);
    			map.put("type", "query");
    			map.put("data", listMap);
            }
            else {
            	map.put("type", "non-query");
            	map.put("data", stmt.getUpdateCount());
            }
        } finally{
        	JDBCUtil.free(rs, stmt, conn);
        }
        return map;
    }

	/**
	 * 交给子类去实现结果集到实体集的转换
	 * @param rs
	 * @return 实体集
	 */
	abstract protected List<T> toEntities(RowSet rs);
}
